package com.xf.common.security.starter.configure;

import com.xf.common.security.starter.handler.FebsAccessDeniedHandler;
import com.xf.common.security.starter.handler.FebsAuthExceptionEntryPoint;
import com.xf.common.security.starter.handler.PcSecurityExpressionHandler;
import com.xf.common.security.starter.properties.FebsCloudSecurityProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

/**
 * @author xufeng
 * 资源服务器配置
 */
@Slf4j
@EnableResourceServer
@EnableAutoConfiguration(exclude = UserDetailsServiceAutoConfiguration.class)
@AllArgsConstructor
public class ResourceServerConfigure extends ResourceServerConfigurerAdapter {

    private FebsCloudSecurityProperties properties;
    private FebsAccessDeniedHandler accessDeniedHandler;
    private FebsAuthExceptionEntryPoint exceptionEntryPoint;
    private PcSecurityExpressionHandler expressionHandler;


    @Override
    public void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        for (String au : properties.getAnonUris()) {
            http.authorizeRequests().antMatchers(au).permitAll();
        }
        http.authorizeRequests().antMatchers().authenticated().and().httpBasic().disable();
        registry.anyRequest().access("@rbacService.hasPermission(request,authentication)");

    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        // 当资源服务器认证失败时返回消息
        if (exceptionEntryPoint != null) {
            resources.authenticationEntryPoint(exceptionEntryPoint);
        }
        if (accessDeniedHandler != null) {
            resources.accessDeniedHandler(accessDeniedHandler);
        }
        if (expressionHandler != null) {
            resources.expressionHandler(expressionHandler);
        }
        resources.tokenServices(new LocalResourceServerTokenServices(jwtAccessTokenConverter()));
    }

    private void permitAll(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests().anyRequest().permitAll();
    }


    /**
     * 加载解密的token转换器
     *
     * @return
     */
    private JwtAccessTokenConverter jwtAccessTokenConverter() {
        final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setVerifier(new org.springframework.security.jwt.crypto.sign.RsaVerifier(retrievePublicKey()));
        return jwtAccessTokenConverter;
    }


    /**
     * Description: 获取公钥 (Verifier Key)<br>
     * Details: 启动时调用
     */
    private String retrievePublicKey() {

        final ClassPathResource classPathResource = new ClassPathResource("public.key");
        // ~ 先从本地取读取名为 authorization-server.pub 的公钥文件, 获取公钥
        try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(classPathResource.getInputStream()))) {
            log.debug("从本地获取公钥 ...");
            return bufferedReader.lines().collect(Collectors.joining("\n"));
        } catch (IOException e) {
            log.error("读取公钥失败,{}", e.getMessage());
            throw new RuntimeException("读取公钥失败");
//            // 如果本地没有, 则尝试通过授权服务器的 租户配置
//            log.debug("从本地获取公钥失败: {}, 尝试从授权服务器 租户信息 端点获取 ...", e.getMessage());
//            final RestTemplate restTemplate = new RestTemplate();
//            final String responseValue = restTemplate.getForObject("AUTHORIZATION_SERVER_TOKEN_KEY_ENDPOINT_URL", String.class);
//
//            log.debug("授权服务器返回原始公钥信息: {}", responseValue);
//            return JSON.parseObject(JSON.parseObject(responseValue).getString("data")).getString("value");
        }
    }
}
